home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Frameworks / Hsoi's App Shell 1.0a4 / Hsoi's App Shell Source / HASMovableModal.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-01-28  |  15.0 KB  |  602 lines  |  [TEXT/CWIE]

  1. /*
  2.     HASMovableModal.c from Hsoi's App Shell ©1995-1997 John C. Daub.  All rights reserved.
  3.     
  4.     This file is a nice "all in one" unit that allows you to impliment a movable
  5.     modal dialog with ease...well, almost.
  6.     
  7.     To begin, this source is actually not my work, but rather the work of Marco Piovanelli.
  8.     This is from his MovableModal library, version 2.0.  However, to get the code to work
  9.     "nicely" with Hsoi's App Shell, I had to modify things here and there.
  10.     
  11.     Plus, I wanted to move towards Copland compatability...so, I added that in as best
  12.     as I could.  And, I also tried to add in more comments to the code to make it
  13.     more understandable to the casual reader.
  14.     
  15.     Furthermore, and I dunno if the problem lies with me or Marco's code (haven't had the
  16.     time to do in-depth investigation right now), but I can't get the disabling of
  17.     the menu bar to work...perhaps cause I'm doing something "wrong" in my code.
  18.     
  19.     Anyways, I use this, currently, to display the "Other Font Size" dialog box...and
  20.     deal with things fine.  Try it out and all credits to Marco for it.
  21.  
  22.     Copyright © 1993-1995 Marco Piovanelli
  23.     <URL:mailto:piovanel@dsi.unimi.it>
  24.  */
  25.  
  26. #pragma mark ••• #includes •••
  27.  
  28. // make sure we have some needed Universal Headers
  29.  
  30.  
  31. #ifndef __TYPES__
  32. #include <Types.h>
  33. #endif
  34.  
  35. #ifndef __EVENTS__
  36. #include <Events.h>
  37. #endif
  38.  
  39. #ifndef __WINDOWS__
  40. #include <Windows.h>
  41. #endif
  42.  
  43. #ifndef __TEXTEDIT__
  44. #include <TextEdit.h>
  45. #endif
  46.  
  47. #ifndef __MENUS__
  48. #include <Menus.h>
  49. #endif
  50.  
  51. #ifndef __DIALOGS__
  52. #include <Dialogs.h>
  53. #endif
  54.  
  55. #ifndef __SCRAP__
  56. #include <Scrap.h>
  57. #endif
  58.  
  59. #ifndef __BALLOONS__
  60. #include <Balloons.h>
  61. #endif
  62.  
  63. #ifndef __LOWMEM__
  64. #include <LowMem.h>
  65. #endif
  66.  
  67. #ifndef __GESTALT__
  68. #include <Gestalt.h>
  69. #endif
  70.  
  71.  
  72. // and don't forget the local header
  73.  
  74. #include "HASMovableModal.h"
  75.  
  76. // if you use Michael Kamprath's WASTE Object Handlers, you'll need to include this
  77.  
  78. #include "WASTE_Objects.h"
  79.  
  80.  
  81. #pragma mark -
  82. #pragma mark ••• Constants •••
  83.  
  84. enum {
  85.     kSystemMenuThreshold    =    -16000, // certain system menus (like the application, help,
  86.                                         // and keyboard menus) have ID's below this number,
  87.                                         // and we don't want to affect those menus in a
  88.                                         // movable modal dialog
  89.                                         
  90.     kMovableModalEventMask = mDownMask + mUpMask + keyDownMask + keyUpMask + autoKeyMask + updateMask + activMask + osMask
  91.     // this is an event mask to filter out the stuff we want/don't want
  92. };
  93.  
  94. #pragma mark -
  95. #pragma mark ••• Structs •••
  96.  
  97.  
  98. // some structs to hold internal data
  99.  
  100. // I added this #pragma in here to help struct alignment for 68k/PPC code happiness
  101.  
  102. #if PRAGMA_ALIGN_SUPPORTED
  103. #pragma options align=mac68k
  104. #endif
  105.  
  106.  
  107. typedef struct {
  108.     MenuRef hMenu;
  109.     short leftEdge;
  110. } MenuEntry;
  111.  
  112. typedef struct {
  113.     short offsetToLastMenu;
  114.     short rightmostEdge;
  115.     short unused;
  116.     MenuEntry theMenus[1];
  117. } MenuList, *MenuListPtr, **MenuListHandle;
  118.  
  119. typedef struct {
  120.     unsigned long mbsBarEnable;
  121.     unsigned long mbsEditEnable;
  122.     short mbsEditMenuID;
  123.     short mbsCutItem;
  124.     short mbsCopyItem;
  125.     short mbsPasteItem;
  126.     Boolean mbsIsDisabled;
  127. } MenuBarState;
  128.  
  129.  
  130. #if PRAGMA_ALIGN_SUPPORTED
  131. #pragma options align=reset
  132. #endif
  133.  
  134. #pragma mark -
  135. #pragma mark ••• Globals •••
  136.  
  137. // a nice global instance of our MenuBarState, initalized happily
  138.  
  139. static MenuBarState barState = { 0, 0, 0, 0, 0, 0, false };
  140.  
  141. #pragma mark -
  142. #pragma mark ••• Static prototypes •••
  143.  
  144. // some local/internal function prototypes
  145.  
  146. short GetMenuItemFromKeyEquiv(MenuRef menu, short keyEquiv);
  147. Boolean DoMenuChoice(DialogRef dialog, short *item, long menuChoice);
  148. Boolean HandleMouseDown(DialogRef dialog, EventRecord *event, short *item);
  149.  
  150. #pragma mark -
  151. #pragma mark ••• Menus •••
  152.  
  153. // given a menu and a keyboard eqivalent (e.g. keyEquiv = X; for the Cut command)
  154. // find the menu item that corresponds to this equivalent.  This is nice cause you
  155. // can't always assume menus will be set up in the same way (e.g. "usually" Cut is
  156. // the 3rd item in the Edit menu, but what if there was no seperator line between
  157. // Undo and Cut?  it'd be the 2nd...this is handy for prevention of assuming
  158. // cause when you assume.... (you make an "ass" out of "u" and "me").
  159.  
  160. // usage: (as an example) GetMenuItemFromKeyEquiv( GetMenuHandle( mEdit ), 'X' );
  161. // and do watch case-sensativity of the keyEquiv!!
  162.  
  163. short GetMenuItemFromKeyEquiv(MenuRef menu, short keyEquiv)
  164. {
  165.     short nItems = CountMItems(menu);
  166.     short item;
  167.     short cmd;
  168.     
  169.     // cycle through the menu getting each item's associated cmdKey equiv (if any)
  170.     
  171.     for ( item = 1; item <= nItems ; item++ )
  172.     {
  173.         GetItemCmd(menu, item, &cmd);
  174.         
  175.         // if the gotten item's cmd key equiv == the desired keyEquiv, return it
  176.         
  177.         if (cmd == keyEquiv)
  178.             return item;
  179.     }
  180.     return 0;
  181. }
  182.  
  183. // see HASMovableModal.h for more overall explanation of this function
  184.  
  185. pascal void DisableMenuBar(short editMenuID, short hmnuID)
  186. {
  187.     DialogRef dialog;
  188.     MenuListHandle menuList;
  189.     MenuRef menu;
  190.     short nMenus;
  191.     short i;
  192.     short menuID;
  193.     Boolean needEditMenu;
  194.     
  195.     // get a DialogRef to the dialog (we should be able to safely assume the dialog
  196.     // will be the front window since you should call DisableMenuBar as per
  197.     // the directions in HASMovableModal.h)
  198.     
  199.     dialog = (DialogRef)FrontWindow();
  200.     
  201.     // might we need the Edit menu?  if we have any edittext fields (and we have a dialog)
  202.     // we will.
  203.  
  204.     needEditMenu = (dialog != NULL) && (GetDialogKeyboardFocusItem(dialog) >= 0);
  205.         
  206.     // grab the list of all the menus
  207.     
  208.     menuList = (MenuListHandle) LMGetMenuList();
  209.     
  210.     // figure out how many menus are in the list
  211.     
  212.     nMenus = (*menuList)->offsetToLastMenu / (short) sizeof(MenuEntry);
  213.     
  214.     // initialize our global menu bar state variable
  215.     
  216.     barState.mbsBarEnable = 0UL;
  217.     barState.mbsEditEnable = 0UL;
  218.     barState.mbsEditMenuID = 0;
  219.     barState.mbsCutItem = 0;
  220.     barState.mbsCopyItem = 0;
  221.     barState.mbsPasteItem = 0;
  222.     barState.mbsIsDisabled = true;
  223.  
  224.     // cycle through all the menus collecting information and disabling things as
  225.     // needed
  226.     
  227.     for ( i = 0; i < nMenus; i++ )
  228.     {
  229.         // get the menu
  230.         
  231.         menu = (*menuList)->theMenus[i].hMenu;
  232.         
  233.         // get it's id
  234.         
  235.         menuID = (*menu)->menuID;
  236.         
  237.         // if it's a system menu (e.g. application, help, keyboard) ignore it and
  238.         // continue
  239.         
  240.         if (menuID <= kSystemMenuThreshold)
  241.             continue;
  242.         
  243.         if ((needEditMenu) && (menuID == editMenuID))
  244.         {
  245.             // if we need the edit menu, save off some information about it
  246.         
  247.             barState.mbsEditMenuID = editMenuID;
  248.             barState.mbsEditEnable = (*menu)->enableFlags;
  249.             barState.mbsCutItem = GetMenuItemFromKeyEquiv(menu, 'X');
  250.             barState.mbsCopyItem = GetMenuItemFromKeyEquiv(menu, 'C');
  251.             barState.mbsPasteItem = GetMenuItemFromKeyEquiv(menu, 'V');
  252.             (*menu)->enableFlags = 1UL + (1UL << barState.mbsCutItem) + (1UL << barState.mbsCopyItem) + (1UL << barState.mbsPasteItem);
  253.             continue;
  254.         }
  255.         
  256.         // check to see if the menu is enabled  (in bit zero, if the bit is set/on,
  257.         // the menu is enabled)
  258.         
  259.         if ((*menu)->enableFlags & 0x00000001)
  260.         {
  261.             // if so, disable it
  262.             
  263.             barState.mbsBarEnable |= (1L << i);
  264.             DisableItem(menu, 0);
  265.         }
  266.         
  267.         // if there is a hmnu resource (balloon help strings) for this menu,
  268.         // then set it up.
  269.         
  270.         HMSetMenuResID(menuID, hmnuID);
  271.     } /* for */
  272.     
  273.     // unhilight the menu
  274.     
  275.     HiliteMenu(0);
  276.     
  277.     // and redraw the new menubar
  278.     
  279.     DrawMenuBar();
  280.     
  281.     return;
  282. }
  283.  
  284. // see HASMovableModal.h for more overall information about this function
  285.  
  286. pascal void ReEnableMenuBar()
  287. {
  288.     MenuListHandle menuList;
  289.     MenuRef menu;
  290.     short menuID;
  291.     short nMenus;
  292.     short i;
  293.     
  294.     // get the list of menus
  295.     
  296.     menuList = (MenuListHandle) LMGetMenuList();
  297.     
  298.     // find out how many menus are in the menubar
  299.     
  300.     nMenus = (*menuList)->offsetToLastMenu / (short) sizeof(MenuEntry);
  301.     
  302.     // if the barState isn't disabled, just return (nothing else to do)
  303.     
  304.     if (!barState.mbsIsDisabled)
  305.         return;
  306.     
  307.     // now cycle through the menus and reenable stuff
  308.     
  309.     for ( i = 0; i < nMenus; i++ )
  310.     {
  311.         // get the menu
  312.         
  313.         menu = (*menuList)->theMenus[i].hMenu;
  314.         
  315.         // get the menu's id
  316.         
  317.         menuID = (*menu)->menuID;
  318.         
  319.         // if the menu is a system menu, ignore it and continue
  320.         
  321.         if (menuID <= kSystemMenuThreshold)
  322.             continue;
  323.         
  324.         // enable things based upon the saved states
  325.         
  326.         if (menuID == barState.mbsEditMenuID)
  327.             (*menu)->enableFlags = barState.mbsEditEnable;
  328.         else if (barState.mbsBarEnable & (1UL << i))
  329.             EnableItem(menu, 0);
  330.         
  331.         // remove the help menu strings
  332.         
  333.         HMSetMenuResID(menuID, -1);
  334.     } /* for */
  335.  
  336.     // and draw the new menu bar
  337.     
  338.     DrawMenuBar();
  339.     
  340.     return;
  341. }
  342.  
  343. // does just that...based upon a menu choice (by mouseDown in the menu bar or
  344. // a cmd key equiv), do the menu command
  345.  
  346. Boolean DoMenuChoice(DialogRef dialog, short *item, long menuChoice)
  347. {
  348.     short menuID = (menuChoice & 0xFFFF0000) >> 16;
  349.     short menuItem = (menuChoice & 0x0000FFFF);
  350.     short currentEditField;
  351.     short itemType;
  352.     Handle itemHandle;
  353.     Rect itemRect;
  354.  
  355.     // unhilite the menu bar
  356.     
  357.     HiliteMenu(0);
  358.     
  359.     // we only handle menu commands from the edit menu, and then, only cut, copy, and
  360.     // paste
  361.     
  362.     if (menuID == barState.mbsEditMenuID)
  363.     {
  364.         // find out what the current/active edit field is
  365.         
  366.         currentEditField = GetDialogKeyboardFocusItem(dialog);
  367.         
  368.         GetDialogItem(dialog, currentEditField, &itemType, &itemHandle, &itemRect);
  369.         if ((itemType & kItemDisableBit) == 0)
  370.             *item = currentEditField;
  371.         
  372.         // and depending on what the desired command is, do it
  373.         
  374.         if (menuItem == barState.mbsCutItem)
  375.         {
  376.             DialogCut(dialog);
  377.             ZeroScrap();
  378.             TEToScrap();
  379.         }
  380.         else if (menuItem == barState.mbsCopyItem)
  381.         {
  382.             DialogCopy(dialog);
  383.             ZeroScrap();
  384.             TEToScrap();
  385.         }
  386.         else if (menuItem == barState.mbsPasteItem)
  387.         {
  388.             DialogPaste(dialog);
  389.         }
  390.         
  391.         return true;
  392.     }    /* if menuID == mbsEditMenuID */
  393.     else
  394.         return false;
  395. }
  396.  
  397. #pragma mark -
  398. #pragma mark ••• Events •••
  399.  
  400. // our mouseDown event handler
  401.  
  402. Boolean HandleMouseDown(DialogRef dialog, EventRecord *event, short *item)
  403. {
  404.     WindowRef window;
  405.     short partCode;
  406.     SoundUPP beeper;
  407.     Rect dragRect;
  408.     RgnHandle windStrucRgn;
  409.     
  410.     // find the window clicked on (or at least, where the mouseDown occured)
  411.     
  412.     partCode = FindWindow(event->where, &window);
  413.     
  414.     // if in the menuBar, do the command and return
  415.     
  416.     if (partCode == inMenuBar)
  417.         return DoMenuChoice(dialog, item, MenuSelect(event->where));
  418.     
  419.     // COPLAND - the following line breaks under STRICT macros
  420. //    if (!PtInRgn(event->where, ((WindowPeek)dialog)->strucRgn))
  421.  
  422.     // since it breaks under STRICT/Copland stuff, here's how we do it.
  423.     
  424.     // first, get a new region
  425.     
  426.     windStrucRgn = NewRgn();
  427.     
  428.     // now, get the structure region of our dialog
  429.     
  430.     GetWindowStructureRgn( GetDialogWindow( dialog ), windStrucRgn );
  431.     
  432.     // if the mouseDown didn't occur within our dialog (and also, not within the
  433.     // menubar, but we took care of the menubar case above so no additional need
  434.     // to check for it here)....
  435.     
  436.     if ( !PtInRgn( event->where, windStrucRgn ) )
  437.     {
  438.         // and if it was within our application's layer, beep (like if they clicked
  439.         // on another one of the app's windows...but of course, we do allow layer/context
  440.         // switching...joys of multitasking!)
  441.         
  442.         // get the beep sound
  443.         
  444.         beeper = (SoundUPP) LMGetDABeeper();
  445.         
  446.         // if we have it, beep
  447.         
  448.         if (beeper)
  449.             CallSoundProc(beeper, 1);
  450.         
  451.         // turn our event into a null event
  452.         
  453.         event->what = nullEvent;
  454.         
  455.         // and dispose of our region
  456.         
  457.         if ( windStrucRgn != nil )
  458.         {
  459.             DisposeRgn( windStrucRgn );
  460.             
  461.             // i want to make sure the windStrucRgn variable is set to nil cause
  462.             // later on we again check if (windStrucRgn != nil) and we don't want
  463.             // the chance of disposing of an already disposed of region!
  464.             
  465.             windStrucRgn = nil;
  466.         }
  467.         return false;
  468.     }
  469.     
  470.     // if they clicked in the drag region (in the title bar)...
  471.     
  472.     if ((partCode == inDrag) && (dialog == (DialogRef)window))
  473.     {
  474.         // set the boundries of the possible area to drag within to all the
  475.         // monitor real estate we have (this is nice for multiple monitor setups).
  476.         
  477.         // but one note...getting the dragRect this way assumes we have color quickdraw
  478.         // on the computer.
  479.  
  480. //        dragRect = (*GetGrayRgn())->rgnBBox;
  481.  
  482.         // so, to be a bit more proper just in case we want to use this code on machines
  483.         // that might not have color quickdraw (this will soon probably be a moot thing
  484.         // to check for), let's do this:
  485.         
  486.         long    response;
  487.     
  488.         // check for the presence of color quickdraw
  489.         
  490.         if ((Gestalt(gestaltQuickdrawVersion, &response) == noErr) && (response >= gestalt8BitQD))
  491.         {
  492.             // if no gestalt errors and we're at least in color qd
  493.             // then we can set it this way (once again, to happily handle multi monitor
  494.             // setups)
  495.             
  496.             dragRect = (*GetGrayRgn())->rgnBBox;
  497.         }
  498.         else
  499.         {
  500.             // no color qd or some gestalt problem...so we'll use the good old screenBits.bounds
  501.             // (as a note, just using this can still work in multiple monitor setups, but
  502.             // that's only due to a kludge that Apple wrote into the system software...
  503.             // it's messy, and someday Apple will probably remove it)
  504.             
  505.             dragRect = qd.screenBits.bounds;
  506.         }
  507.         
  508.         // that was something I added into this (Marco just had the straight GetGrayRgn thing)
  509.         // and i did this as per some "morally correct" code Eric Shapiro gave in the
  510.         // July 1994 MacTech Magazine.  I do this same thing elsewhere in Hsoi's App
  511.         // Shell, in HsoiDoDrag()
  512.         
  513.         // now just let the toolbox handle the drag
  514.         
  515.         DragWindow(window, event->where, &dragRect);
  516.         
  517.         // and make the event a null event
  518.         
  519.         event->what = nullEvent;
  520.     }
  521.     
  522.     // once again, if we have a region, dispose of it (we don't need no stinkin' memory leaks!)
  523.     
  524.     if ( windStrucRgn != nil )
  525.         DisposeRgn( windStrucRgn );
  526.         
  527.     return false;
  528. }
  529.  
  530. #pragma mark -
  531. #pragma mark ••• MovableModalDialog •••
  532.  
  533. // and the meat of it all...the routine that actually does the movable modal dialog.
  534. // do see HASMovableModal.h for more information.
  535.  
  536. pascal void MovableModalDialog(ModalFilterUPP filterProc, short *item)
  537. {
  538.     GrafPtr savePort;
  539.     DialogRef dialog, junkDialog;
  540.     EventRecord event;
  541.     
  542.     // hopefully, any routine calling this function will have already stopped the
  543.     // sound, but just in case, we'll put this here. (this is from Michael Kamprath's
  544.     // WASTE Object Handlers)
  545.     
  546.     if ( SoundIsPlaying() )
  547.         StopCurrentSound();
  548.     
  549.     // initalize our item to zero
  550.     
  551.     *item = 0;
  552.     
  553.     // get a ref to our dialog (we will assume it's frontmost)
  554.     
  555.     dialog = (DialogRef)FrontWindow();
  556.     
  557.     // if we don't have it, something's dead wrong
  558.     
  559.     if (!dialog)
  560.         return;
  561.     
  562.     // set up the ports
  563.     
  564.     GetPort(&savePort);
  565.     SetGrafPortOfDialog(dialog);
  566.     
  567.     // and now loop until something exciting happens.
  568.     
  569.     do {
  570.     
  571.         // get the next event as filtered by our event mask
  572.         
  573.         WaitNextEvent(kMovableModalEventMask, &event, 0, NULL);
  574.         
  575.         // call the filter
  576.         
  577.         if (filterProc && CallModalFilterProc(filterProc, dialog, &event, item))
  578.             break;
  579.         
  580.         // if we have a mouseDown event and we handle it right...
  581.         
  582.         if ((event.what == mouseDown) && HandleMouseDown(dialog, &event, item))
  583.             break;
  584.         
  585.         // a keydown, with the command key, and do the menu choice then...
  586.         
  587.         if ((event.what == keyDown) && (event.modifiers & cmdKey) && DoMenuChoice(dialog, item, MenuKey(event.message & charCodeMask)))
  588.             break;
  589.         
  590.         // and finally see if the event pertains to our dialog, then find the
  591.         // dialog and process the event
  592.         
  593.         if (IsDialogEvent(&event) && DialogSelect(&event, &junkDialog, item))
  594.             break;
  595.     } while (true);
  596.  
  597.     // restore the port, and that's it!
  598.     
  599.     SetPort(savePort);
  600.     
  601.     return;
  602. }